package com.google.code.p.restrpc.client.cluster;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public final class FailureRecoveryStrategyManager {
    
    private static final Log logger = LogFactory.getLog(FailureRecoveryStrategyManager.class);
    
    static class FailureRecoveryStrategyFactoryHolder {
        static FailureRecoveryStrategyManager instance = new FailureRecoveryStrategyManager();
    }
    
    public static FailureRecoveryStrategyManager getInstance() {
        return FailureRecoveryStrategyFactoryHolder.instance;
    }
    
    private Map<String, FailureRecoveryStrategy> strategies = new HashMap<String, FailureRecoveryStrategy>();
    
    private FailureRecoveryStrategyManager() {
        InputStream is = null;
        try {
            is = FailureRecoveryStrategyManager.class.getResourceAsStream("/META-INF/configuration/failurerecovery.strategy.properties");
            if (is == null) {
                throw new FailureRecoveryStrategyConfigurationException("/META-INF/configuration/failurerecovery.strategy.properties not found!");
            }
            Properties properties = new Properties();
            properties.load(is);
            initStrategies(properties);
        }
        catch (IOException e) {
            throw new FailureRecoveryStrategyConfigurationException(e);
        }
        finally {
            if (null != is) {
                try {
                    is.close();
                }
                catch (Exception e) {
                }
            }
        }
    }
    
    private void initStrategies(Properties properties) {
        for (Iterator iterator = properties.entrySet().iterator(); iterator.hasNext();) {
            try {
                Map.Entry<String, String> entry = (Map.Entry<String, String>) iterator.next();
                String className = entry.getValue();
                if (StringUtils.isEmpty(className)) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Caution:no implementation class specified for " + entry.getKey());
                    }
                    continue;
                }
                Class strategyClass = Class.forName(className);
                Object strategyInstance = strategyClass.newInstance();
                if (!(strategyInstance instanceof FailureRecoveryStrategy)) {
                    throw new FailureRecoveryStrategyConfigurationException("The class for " + entry.getKey()
                                                                            + " is not implemented "
                                                                            + FailureRecoveryStrategy.class.getName());
                }
                strategies.put(entry.getKey(),
                               (FailureRecoveryStrategy) strategyInstance);
            }
            catch (ClassNotFoundException e) {
                throw new FailureRecoveryStrategyConfigurationException(e);
            }
            catch (InstantiationException e) {
                throw new FailureRecoveryStrategyConfigurationException(e);
            }
            catch (IllegalAccessException e) {
                throw new FailureRecoveryStrategyConfigurationException(e);
            }
        }
    }
    
    public FailureRecoveryStrategy getStrategy(String strategyName) {
        return strategies.get(strategyName);
    }
    
    public FailureRecoveryStrategy getStrategy(FailureRecovery failureRecovery) {
        return getStrategy(failureRecovery.value());
    }
    
    public static void main(String[] args) {
        System.out.println(FailureRecoveryStrategyManager.getInstance()
                                                         .getStrategy(FailureRecovery.FailOver));
        System.out.println(FailureRecoveryStrategyManager.getInstance()
                                                         .getStrategy(FailureRecovery.FailFast));
        System.out.println(FailureRecoveryStrategyManager.getInstance()
                                                         .getStrategy(FailureRecovery.FailSafe));
        System.out.println(FailureRecoveryStrategyManager.getInstance()
                                                         .getStrategy(FailureRecovery.FailBack));
    }
}
